/**
 * Table of ItemClasses
 * 
 * @author remo.lemma@lu.unisi.ch
 */

package ch.unisi.inf.projects.java.uom.gui.table;

import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Set;
import java.util.HashSet;
import java.util.Comparator;
import java.util.regex.Pattern;

import org.jdesktop.swingx.*;
import org.jdesktop.swingx.decorator.*;

import ch.unisi.inf.projects.java.uom.elements.*;
import ch.unisi.inf.projects.java.uom.gui.commands.*;
import ch.unisi.inf.projects.java.uom.gui.table.itemclass.*;

public class UOMItemClassJTable extends JXTable implements DocumentListener {

       /** We are not going to serialize this class atm */
   private static final long serialVersionUID = -1L;
   
   private static final Dimension STANDARD_DIMENSION = new Dimension(200,0);
   
   private static final ItemClass allCat = new ItemClass("All Categories");
   
   private final List<UOMItemClassJTableListener> listeners;
       
       public UOMItemClassJTable ()
       {
               super(new UOMItemClassTableModel());
               final UOMItemClassTableModel model = (UOMItemClassTableModel) this.getModel();
               model.addColumn("Categories");
               this.setDefaultRenderer(ItemClass.class, new UOMItemClassTableCellRenderer());
               this.toggleSortOrder(0);
               this.setColumnControlVisible(true);
               this.setColumnControl(new UOMItemClassColumnControlButton(this));
               this.setRowHeight(48); // We know this from icon
               
               listeners = new ArrayList<UOMItemClassJTableListener>();
               this.addMouseListener(new MouseAdapter () {
                       public void mousePressed (final MouseEvent ev) { checkPopup(ev); }
                       public void mouseReleased (final MouseEvent ev) { checkPopup(ev); }
                       private void checkPopup (final MouseEvent ev) { if (ev.isPopupTrigger()) showPopup(ev.getPoint()); }
               });
               ItemManager.instance().addItemManagerListener(new ItemManagerListener() {
                       public void itemAdded (final Item i) { return; }
                       public void focusOnItem (final Item i) { return; }
                       public void itemClassAdded (final ItemClass c) { model.addRow(c); }
                       public void elementDeleted (final Element e) { model.removeRow(e);  }
                       public void elementModified (final Element e) { model.updateRow(e); }
                       public void databaseOpened () { setEnabled(true); init(); }
                       public void databaseClosed () { model.clear(); setEnabled(false); } });
               this.addKeyListener(new KeyAdapter() {
                       public void keyPressed (final KeyEvent ev) {
                               if (ev.getKeyCode() == KeyEvent.VK_D && ev.getModifiers() == Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())
                                       new ElementDeleteCommand(new HashSet<Element>(getSelectedItemClasses())).actionPerformed(null);
                               else if (ev.getKeyCode() == KeyEvent.VK_N && ev.getModifiers() == Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())
                            	   new ItemCreateCommand((ItemClass) getValueAt(getSelectedRow(), 0),false).actionPerformed(null);
                       }
               });
               this.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
                       public void valueChanged (final ListSelectionEvent e) {
                               final int[] rows = getSelectedRows();
                               for (final int row : rows) {
                                       if (row == 0)
                                               getSelectionModel().setSelectionInterval(0, 0);
                               }
                               fireItemClassesSelected();
                       } });
       }
       
       private void showPopup (final Point p)
       {
               final JPopupMenu popup = new JPopupMenu();
               final JMenuItem delete = new JMenuItem();
               final int row = rowAtPoint(p);
               final ListSelectionModel model = this.getSelectionModel();
               final ElementDeleteCommand cmd = new ElementDeleteCommand(new HashSet<Element>(this.getSelectedItemClasses()));
               
               if (!model.isSelectedIndex(row))
                       model.setSelectionInterval(row, row);
               for (final ItemClass c : this.getSelectedItemClasses()) {
            	   if (c.getName().equals(allCat.toString().toUpperCase()))
            		   return;
               }
               if (this.getSelectedRows().length == 1) {
                       final JMenuItem add = new JMenuItem(new ItemCreateCommand((ItemClass) this.getValueAt(row, 0), false));
                       popup.add(add);
                       popup.addSeparator();
                       popup.add(new JMenuItem(new ItemClassGUICreateCommand((ItemClass) this.getValueAt(row, 0))));
                       popup.addSeparator();
                       cmd.setNameAndDesc("Delete Category", "Deletes this category");
               }
               else
                       cmd.setNameAndDesc("Delete Selected Categories", "Deletes the selected categories");
               delete.setAction(cmd);
               popup.add(delete);
               popup.show(this, (int) p.getX(), (int) p.getY());
       }
       
       public void toggleSortOrder (final int col)
       {
       final Comparator<ItemClass> comparator = new Comparator<ItemClass>() {
           public int compare (final ItemClass c1, final ItemClass c2) {
               int val = 1;
               if (getSortOrder(col) == org.jdesktop.swingx.decorator.SortOrder.ASCENDING)
            	   val = -val;
               if (c1.getName().toUpperCase().equals(allCat.getName().toUpperCase())) return val;
               else if (c2.getName().toUpperCase().equals(allCat.getName().toUpperCase())) return -val;
               else return c1.toString().compareTo(c2.toString()); } };
       this.getColumnExt(col).setComparator(comparator);
       super.toggleSortOrder(col);
       }
       
       public Dimension getPreferredScrollableViewportSize ()
       {
               return STANDARD_DIMENSION;
       }

       public void init()
       {
               final UOMItemClassTableModel model = (UOMItemClassTableModel) this.getModel();
               model.clear();
               try {
            	   final List<ItemClass> itemClasses = Arrays.asList(ItemManager.instance().getItemClasses().toArray(new ItemClass[] {}));
            	   model.addRow(allCat);
            	   for (final ItemClass c : itemClasses)
            		   model.addRow(c);
               }
               catch (Exception e)
               {
                       //TODO
               }
               getSelectionModel().setSelectionInterval(0, 0);
               fireItemClassesSelected();
       }

       private Set<ItemClass> getSelectedItemClasses ()
       {
               final int[] rows = this.getSelectedRows();
               final Set<ItemClass> itemClasses = new HashSet<ItemClass>(rows.length);
               for (int i = 0; i < rows.length; i++) {
                       final ItemClass itemClass = (ItemClass) this.getValueAt(rows[i], 0);
                       if (itemClass.getName().equals(allCat.getName())) {
                                       for (int j = 0; j < this.getRowCount(); j++)
                                               itemClasses.add((ItemClass) this.getValueAt(j, 0));
                                       return itemClasses;
                       }
                       else
                               itemClasses.add((ItemClass) this.getValueAt(rows[i], 0));
               }
               return itemClasses;
       }
       
       private void fireItemClassesSelected()
       {
               // We use a set because of duplicates
               final Set<ItemClass> itemClasses = getSelectedItemClasses();
               for (final UOMItemClassJTableListener li : listeners)
                       li.itemClassesSelected(this, itemClasses);
       }
       
       private void fireTableShow ()
       {
               for (final UOMItemClassJTableListener li : listeners)
                       li.tableShown(this);
       }
       
       private void fireTableHide ()
       {
               for (final UOMItemClassJTableListener li : listeners)
                       li.tableHidden(this);
       }
       
       public void toShow() 
       {
    	   fireTableShow();
       }
	
       public void toHide()
       {
    	   fireTableHide();
       }
       
       public void addUOMItemClassJTableListener (final UOMItemClassJTableListener listener)
       {
               listeners.add(listener);
       }
       
       public void removeUOMItemClassJTableListener (final UOMItemClassJTableListener listener)
       {
               listeners.remove(listener);
       }
       
       public void applyFilter (final DocumentEvent e)
       {
    	   this.getSelectionModel().removeSelectionInterval(0, this.getRowCount() - 1);
               try {
                       final String filter = e.getDocument().getText(0, e.getDocument().getLength()).toUpperCase();
                       final List<Filter> listOfFilters = new ArrayList<Filter>();
                       if (filter != null && !filter.equals(""))
                               listOfFilters.add(new PatternFilter("^(?!\\Q" + allCat.getName() + "\\E).*$",Pattern.CASE_INSENSITIVE,0));
                       listOfFilters.add(new PatternFilter("^" + filter + ".*",Pattern.CASE_INSENSITIVE,0));
                       this.setFilters(new FilterPipeline(listOfFilters.toArray(new Filter[] {})));
               }
               catch (final Exception ex)
               {
                       return;
               }
       }
       
       public void changedUpdate (final DocumentEvent e) { applyFilter(e); }
       public void insertUpdate (final DocumentEvent e) { applyFilter(e); }
       public void removeUpdate (final DocumentEvent e) { applyFilter(e); }


}